/**
 * 定时器/计数器: https://www.bilibili.com/video/BV1Mb411e7re?p=17
 * 定时器介绍:
 * 51单片机的定时器属于单片机的内部资源，其电路的连接和运转均在单片机内部完成.
 * 定时器作用:
 * (1)用于计时系统，可实现软件计时，或者使程序每隔一固定时间完成一项操作
 * (2)替代长时间的Delay，提高CPU的运行效率和处理速度
 * (3)多任务时间片切换(系统一般都有这功能)
 * (...)
 *
 * 
 * STC89C52 定时器资源
 * 定时器个数 :3个(TO、T1、T2)
 *            TO和T1与传统的51单片机兼容, (有些单片机没有T1)
 *            T2是此型号单片机增加的资源
 * 注意:定时器的资源和单片机的型号是关联在一起的，不同的型号可能会有不同的定时器个数和操作方式，
 *      但一般来说，TO和T1的操作方式是所有51单片机所共有的
 * 
 * 定时器在单片机内部就像一个小闹钟一样，根据时钟的输出信号, 
 * 每隔“1个固定时间”，计数单元的数值就增加一，当计数单元数值增加到“设定的闹钟提醒时间”时，
 * 计数单元就会向"中断系统"发出中断申请, 产生“响铃提醒”，使程序跳转到"中断服务函数"中执行
 * 
 * 时钟 ----------->  技术单元 -----------> 中断系统
 * 提供技术单元        时钟技术              产生中断,
 *  的时钟脉冲                             执行定时任务
 * 
 * 
 * STC89C52的TO和T1均有四种工作模式(TMOD)
 * 模式0: 13位定时器/计数器 (基本用不到)
 * 模式1: 16位定时器/计数器 (常用)
 * 模式2: 8位自动重装模式   (用得少)
 * 模式3: 两个8位计数器     (基本用不到)
 * 
 * 
 * 
 * 第6章: 中断系统: (来自"STC89c52.pdf")
 *     中断系统是为使CPU具有对外界紧急事件的实时处理能力而设置的。
 *     当中央处理机CPU正在处理某件事的时候外界发生了紧急事件请求，要求CPU暂停当前的工作，
 * 转而去处理这个紧急事件，处理完以后，再回到原来被中断的地方，继续原来的工作，
 * 这样的过程称为中断。实现这种功能的部件称为中断系统，请示CPU中断的请求源称为中断源。
 * 微型机的中断系统一般允许多个中断源，当几个中断源同时向CPU请求中断，要求为它服务的时候，
 * 这就存在CPU优先响应哪一个中断源请求的问题。通常根据中断源的轻重缓急排队，
 * 优先处理最紧急事件的中断请求源，即规定每一个中断源有一个优先级别。
 * CPU总是先响应优先级别最高的中断请求。
 *     当CPU正在处理一个中断源请求的时候(执行相应的中断服务程序)，发生了另外一个优先级比它还高的中断源请求。
 * 如果CPU能够暂停对原来中断源的服务程序，转而去处理优先级更高的中断请求源，
 * 处理完以后，再回到原低级中断服务程序，这样的过程称为"中断嵌套"。
 * 这样的中断系统称为"多级中断系统"，没有中断嵌套功能的中断系统称为"单级中断系统"。
 * 
 * STC89C52中断资源:
 *   中断源个数：8个（外部中断0、定时器0中断、外部中断1、定时器1中断、串口中断(UART)、定时器2中断、外部中断2、外部中断3）(后2个中断是这个型号新增的)
 *   中断请求标志位:    IE0         TF0         IE1     TF1         R1+T1           TF2 + EXF2  IE2         IE3
 *   中断优先级个数：4个
 *   中断号：见7.3 图片
 * TCON: 定时器/计数器 中断控制寄存器 (可位寻址)
 * SFR name   Address   bit   B7   B6   B5   B4   B3   B2   B1   B0
 *   TCON       88H     name  TF1  TR1  TF0  TR0  IE1  IT1  IE0  IT0
 * 
 * TF1: 定时器/计数器 T1 溢出标志。T1被允许计数以后，从初值开始加1计数。当最高位产生溢
 *      出时由硬件置"1"TF1，向CPU请求中断，一直保持到CPU响应中断时，才由硬件清
 *      "0"TF1（TF1也可由程序查询清"0"）。
 * TR1: 定时器 T1 的运行控制位。该位由软件置位和清0。当GATE（TMOD.7）=0，TR1=1时就
 *      允许T1开始计数，TR1=0时禁止T1计数。当GATE（TMOD.7）=1，TR1=1且INT1输入
 *      高电平时，才允许T1计数。
 * TF0：定时器/计数器T0溢出中断标志。T0被允许计数以后，从初值开始加1计数，当最高位产
 *      生溢出时，由硬件置"1"TF0，向CPU请求中断，一直保持CPU响应该中断时，才由硬
 *      件清"0"TF0（ TF0也可由程序查询清"0"）。
 * TR0: 定时器T0的运行控制位。该位由软件置位和清0。当GATE（TMOD.3）=0，TR0=1时就
 *      允许T0开始计数，TR0=0时禁止T0计数。当GATE（TMOD.3）=1，TR0=0且INT0输入
 *      高电平时，才允许T0计数。
 * IE1：外部中断1请求源（ INT1 /P3.3）标志。IE1=1，外部中断向CPU请求中断，当CPU响应该
 *      中断时由硬件清"0"IE1。
 * IT1：外部中断1触发方式控制位。IT1=0时，外部中断1为低电平触发方式，当INT1（P3.3）
 *      输入低电平时，置位IE1 。采用低电平触发方式时，外部中断源（输入到INT1）必须保
 *      持低电平有效，直到该中断被CPU 响应，同时在该中断服务程序执行完之前，外部中断
 *      源必须被清除(P3.3要变高)，否则将产生另一次中断。当IT1=1时，则外部中断1( INT1 )
 *      端口由"1"→"0"下降沿跳变，激活中断请求标志位IE1，向主机请求中断处理。
 * IE0：外部中断0请求源（INT0/P3.2）标志。IE0=1外部中断0向CPU请求中断，当CPU响应外
 *      部中断时，由硬件清"0"IE0（边沿触发方式）。
 * IT0：外部中断0触发方式控制位。IT0=0时，外部中断0为低电平触发方式，当INT0（P3.2）
 *      输入低电平时，置位IE0。采用低电平触发方式时，外部中断源（输入到INT0）必须保
 * 持低电平有效，直到该中断被CPU响应，同时在该中断服务程序执行完之前，外部中断源
 * 必须被清除（P3.2要变高），否则将产生另一次中断。当IT0=1时，则外部中断0(INT0)
 * 端口"1"→"0"下降沿跳变，激活中断请求标志位IE0 ，向主机请求中断处理。
 * 
 * 
 * 
 * 
 * https://www.bilibili.com/video/BV1Mb411e7re?p=18
 * 第7章 定时器/计数器 (来自"STC89c52.pdf")
 * 7.1.1 定时器/计数器0和1的 相关寄存器
 * 符号         描述    地址        位地址及其符号                  复位值
 *                              MSB                        LSB
 * TCON 定时器控制寄存器 88H    TF1 TR1  TF0 TR0 IE1  IT1 IE0 IT0   0000 0000B
 * TMOD 定时器模式寄存器 89H    GATE C/T M1  M0  GATE C/T M1  M0    0000 0000B
 * TL0  Timer Low 0     8AH                                        0000 0000B
 * TL1  Timer Low 1     8BH                                        0000 0000B
 * TH0  Timer High 0    8CH                                        0000 0000B
 * TH1  Timer High 1    8DH                                        0000 0000B
 * 
 * TMOD: 定时器/计数器 工作模式寄存器 (不可位寻址)
 *  7    6    5   4   3     2    1   0
 * GATE  C/T  M1  M0  GATE  C/T  M1  M0
 *  \   定时器1    /   \   定时器0    /
 * 
 *    位        符号        功能
 * TMOD.7/      GATE    TMOD.7控制定时器1, 置1时只有在INT1=1 & TR1控制位置1
 *                      时才可打开 定时器/计数器1。
 * TMOD.3/      GATE    TMOD.3控制定时器0, 置1时只有在INT0=1 & TR0控制位置1
 *                      时才可打开 定时器/计数器0。
 * TMOD.6/      C/T     TMOD.6控制定时器1用作定时器/计数器，清0则用作定时器
 *                      (从内部系统时钟输入)，置1用作计数器(从T1/P3.5脚输入)
 * TMOD.2/      C/T     TMOD.2控制定时器0用作定时器/计数器，清0则用作定时器
 *                      (从内部系统时钟输入)，置1用作计数器(从T0/P3.4脚输入)
 * TMOD.5/TMOD.4 M1、M0  定时器/计数器1 模式选择
 *               0 0    13位定时器/计数器， 兼容8048定时模式，TL1只用低5位参与
 *                      分频，TH1整个8位全用 。
 *               0 1    16位定时器/计数器，TL1、TH1全用
 *               1 0    8位自动重装载定时器, 当溢出时将TH1存放的值自动重装入TL1
 *               1 1    定时器/计数器1此时无效（停止计数）。
 * TMOD.1/TMOD.0 M1、M0  定时器/计数器0模式选择
 *               0 0    13位定时器/计数器， 兼容8048定时模式，TL0只用低5位参与
 *                      分频，TH0整个8位全用 。
 *               0 1    16位定时器/计数器，TL0、TH0全用
 *               1 0    8位自动重装载定时器，当溢出时将TH0存放的值自动重装入TL0
 *               1 1    定时器0此时作为双8位定时器/计数器。TL0作为一个8位定时
 *                      器/计数器，通过标准定时器0的控制位控制。TH0仅作为一个
 *                      8位定时器, 由定时器1的控制位控制。
 */
#include <REGX52.H>
#include "Head/INTRINS_H.h"
#include "Link/Single_Keyboard_Utils.h"
#include "Link/Timer0_Utils.h"

static void Timer0_Rountine11();

void main_interrupt_counter() {
    //初始化定时器0, 15ms溢出1次
    Timer0_Init(15000, 0);
    Timer0_setOnTickListener_Exact(1, &Timer0_Rountine11);

    //初始化led灯
    P1 = 0xFE;
    //然后进入 while(1)
    while (1) {
        /**
         * 发生中断后, 进入中断函数. 中断函数执行完后, 再次回到 while(1)
         */
    }
}

/**
 * 中断函数
 * interrupt 1: 中断号
 */
static void Timer0_Rountine11() {
    //static, 不会被重复初始化
    unsigned char keyNum, ledMode = 0, t0Count = 0;

    //独立按键 K1, K2, 实现流水灯 左移&右移
    keyNum = Single_Keyboard_Down_Delay(1);
    if (keyNum ==1 || keyNum == 2) {
        ledMode = keyNum - 1;
    }


    //33*15 = 定时495ms, 循环右移 & 左移.  1不会移出去, 会循环移动
    t0Count++;
    if (t0Count >= 33) {
        t0Count = 0;
        if (ledMode) {
            P1 = _cror_(P1, 1);
        } else {
            P1 = _crol_(P1, 1);
        }
    }
}